#include "myShader.h"
#ifndef GL_INCLUDES
#define GL_INCLUDES
#ifdef linux
#include<GL/glew.h>
#include<GLFW/glfw3.h>
#else
#include<glew.h>
#include<glfw3.h>
#endif
#endif

#include<iostream>
#include <fstream>
#include <string.h>    
#include <stdlib.h>
void myShader::setGLShaderType()
{
    switch(shaderType)
    {
        case(vertexShader):
        glShaderType = GL_VERTEX_SHADER;
        break;
    
        case(fragmentShader):
        glShaderType = GL_FRAGMENT_SHADER;
        break;
    
        default:
        std::cout << "\nERROR : Attempting to map invalid shader type " << std::endl;
        
    }
}

myShader::myShader(const int type, const char *name) 
{
    shaderType = type;
    shaderName = name;
    setGLShaderType();
    handle = -1;
    errorcode = 0;
} 

void myShader::readFile(const char *file)
{
    std::ifstream t;
    int length = 0;
    t.open(file);
    if(!t.is_open())
    {
        std::cout << "\nERROR : Unable to open file : " << file << std::endl;
        exit(1);
        return;
    }
    t.seekg(0,std::ios::end);
    length = t.tellg();
    t.seekg(0,std::ios::beg);
    if(length != -1) {
        shaderSource = new char[length+1];
    }
    else {
         std::cout << "\nERROR : Length of file " << file << " is invalid " << std::endl;
    }
    
    t.read(shaderSource, length);
    shaderSource[length]='\0';
    t.close(); 
}

void myShader::copyString(const char *string)
{
    unsigned int length = strlen(string) + 1;
    shaderSource = new char[length];
    strcpy(shaderSource, string); 
}

void myShader::createShaderFromFile(const char *file)
{
    readFile(file);
    handle = glCreateShader(glShaderType);
    if (handle == 0 ) {
        std::cout << "\nERROR : Unable to create handle for shader file : " << file << std::endl;
    }
    glShaderSource(handle, 1, (const GLchar **) &shaderSource, NULL);
    
    if(glGetError() != GL_NO_ERROR) {
        std::cout << "\nERROR : glShaderSource() failed for file : " << file << " of type : " << typeToString();
    }
    
    
}

void myShader::createShaderFromString(const char *string)
{
    handle = glCreateShader(glShaderType);
    if(handle == 0 ) {
        std::cout << "\nERROR : Unable to create handle for shader from string : " << std::endl;
    }
    glShaderSource(handle, 1, (const GLchar **) &shaderSource, NULL);
    
    if(glGetError() != GL_NO_ERROR) {
        std::cout << "\nERROR : glShaderSource() failed for string of type : " << typeToString();
    }
}
    
const int myShader::getType()
{
    return shaderType;
}

void myShader::compileShader()
{
    GLint status = GL_FALSE;
    GLint len = 0;
    GLchar *shaderLog = NULL; 
    if (handle != -1) {
        glCompileShader(handle);
        glGetShaderiv(handle, GL_COMPILE_STATUS, &status);
        if (status == GL_FALSE) {
            std::cout << "\nERROR : " << "[" << typeToString() << "]:::" << shaderName << ":::" << " failed to compile" << std::endl;
            shaderLog = new GLchar[1000];
            glGetShaderInfoLog(handle, 1000, &len, shaderLog);
            if (len != 0) {
                std::cout << "\nSHADER_INFO_LOG : \n" << shaderLog << std::endl;
                exit(1);
            }
            free(shaderLog);
                
        }
        
    }
    else {
        std::cout << "\nERROR : Attempting to compile shader with invalid handle" << std::endl;
        exit(1);
    }
    
}

GLuint myShader::getHandle()
{
    return handle;
}

const char * myShader::typeToString()
{
    switch(shaderType)
    {
        case vertexShader:
            return " Vertex Shader ";
        
        case fragmentShader:
            return " Fragment Shader ";

        default:
            std::cout << "\nERROR : Invalid shader type to represent as string " << std::endl;
            return "";
    }
}
 
//////////////////////////////////////////////////////////////////////////////////
/*                       CLASS MYSHADERPIPE                                     */
//////////////////////////////////////////////////////////////////////////////////
    

myShaderPipe::myShaderPipe()
{
    for(int i = 0 ; i < numStages ; i++)
        shaders[i] = -1;
}       

void myShaderPipe::attachShader(myShader *shader)
{
    if(shader == NULL) return;

    switch(shader->getType())
    {
        case myShader::vertexShader:
        shaders[myShader::vertexShader] = shader->getHandle();
        break;
        
        case myShader::fragmentShader:
        shaders[myShader::fragmentShader] = shader->getHandle();
        break;
    }
}

void myShaderPipe::createProgram() {
    handle = glCreateProgram();
    if (handle == 0) {
        std::cout << "\nERROR: occurred while creating program" << std::endl;
        exit(1);
    }
}
void myShaderPipe::linkPipe()
{
    GLint status = GL_FALSE;
    GLint len = 0;
    GLchar *programLog = NULL; 
    for(int i = 0 ; i < numStages; i++)
    {
        if(shaders[i] != -1) {
            glAttachShader(handle, shaders[i]);
        }
    } 
    glLinkProgram(handle);
    glGetProgramiv(handle, GL_LINK_STATUS, &status);
    if (status == GL_FALSE) {
        std::cout << "\nERROR : Failed to link program " << std::endl;
        programLog = new GLchar[1000];
        glGetProgramInfoLog(handle, 1000, &len, programLog);
        if (len != 0) {
            std::cout << "\nPROGRAM_INFO_LOG : \n" << programLog << std::endl;
            exit(1);
        }
        free(programLog);
    }
}

GLuint myShaderPipe::getHandle()
{
    return handle;
}

